/**
 * 消息解析器
 * @date 2024.9.10
 * @author wz
 */

import { MsgHeadSize, MsgIDSize, MaxMsgSize } from "./SocketConfig";

export class MsgParser {
    /**
     * 用于缓存未完成的消息
     */
    private _unit8Arr: Uint8Array = new Uint8Array(0);


    /**
     * 消息解析
     * @param binary 
     * @param handle 
     */
    parse(binary: ArrayBuffer, handle: (cmd: number, data: Uint8Array) => void) {
        const data = new Uint8Array(binary);
        this._unit8Arr = this.concatUnit8Arrays(this._unit8Arr, data);
        while (this._unit8Arr.length >= MsgHeadSize) {
            //截取消息头，获取消息长度
            const msgSize = this.readLengthFromBuffer(this._unit8Arr.slice(0, MsgHeadSize));
            //完整的消息长度(消息头 + 消息)
            const complateSize = MsgHeadSize + msgSize;
            //有一条完整消息
            if (this._unit8Arr.length >= complateSize) {
                const msgIdArr = this._unit8Arr.slice(MsgHeadSize, MsgHeadSize + MsgIDSize);
                //消息号
                const cmd = this.readLengthFromBuffer(msgIdArr);
                //获取消息
                const message = this._unit8Arr.slice(MsgHeadSize + MsgIDSize, complateSize);
                //更新缓存
                this._unit8Arr = this._unit8Arr.slice(complateSize);
                if (handle) handle(cmd, message);
            } else break;
        }
    }

    /**
     * 消息发送时候包裹
     * @param cmd 
     * @param data 
     * @returns 
     */
    wrap(cmd: number, data: Uint8Array): Uint8Array | undefined {
        //消息的长度
        const msgSize = data.length + MsgIDSize;

        if (msgSize > MaxMsgSize) {
            console.error("消息过大，无法发送");
            return
        }

        //消息头记录整条消息长度buffer
        const headBuffer = this.writeLengthToBuffer(msgSize);
        //消息号buffer
        const msgIdBuffer = this.writeLengthToBuffer(cmd);
        //发送数据
        const wrapData = this.concatUnit8Arrays(headBuffer, msgIdBuffer, data);
        return wrapData;
    }

    /**
     * 清空缓存
     */
    clear() {
        this._unit8Arr = new Uint8Array(0);
    }


    /**
     * 从一个Uint8Array中读取长度
     * @param arr 
     * @returns 
     */
    private readLengthFromBuffer(arr: Uint8Array): number {
        return new DataView(arr.buffer).getInt32(0, true);
    }

    /**
     * 将长度写入一个Uint8Array
     * @param length 
     * @returns 
     */
    private writeLengthToBuffer(length: number): Uint8Array {
        const view = new DataView(new ArrayBuffer(MsgIDSize));
        view.setInt32(0, length, true);
        return new Uint8Array(view.buffer);
    }

    /**
     * 把多个Uint8Array合并成一个
     * @param params 
     * @returns 
     */
    private concatUnit8Arrays(...params: Uint8Array[]) {
        const totalLength = params.reduce((length: number, arr: Uint8Array) => {
            return length + arr.length
        }, 0)
        const arr = new Uint8Array(totalLength);
        let curLength = 0;
        for (let i = 0; i < params.length; i++) {
            arr.set(params[i], curLength);
            curLength += params[i].length;
        }
        return arr;
    }
}


